package com.mattgarner.jaddas.node.data;

import com.mattgarner.jaddas.common.ColumnType;
import com.mattgarner.jaddas.common.ResultSet;
import com.mattgarner.jaddas.dataset.DataSet;
import com.mattgarner.jaddas.dataset.DataSetException;
import com.mattgarner.jaddas.dataset.DataSetPartition;
import com.mattgarner.jaddas.node.LocalDataProvider;
import com.mattgarner.jaddas.node.NodeConfigurationManager;
import com.mattgarner.jaddas.node.index.ColumnIndexStatistics;
import com.mattgarner.jaddas.node.index.LocalIndex;

public class LocalTableDataProvider {
	private DataSet dsHeader;
	private TableHeader dsDataHeader;
	
	private DataSetPartition dsPartition;
	
	//private LocalDataProvider dataProvider;
	//private LocalTable tableData;
	private LocalIndex tableIndex;
	
	public LocalTableDataProvider(DataSet dataSet, int localTablePartitionID) throws DataSetException {
		if(dataSet == null) {
			throw new DataSetException("LocalTableDataProvider: dataSet cannot be null.");
		}
		
		dsHeader = dataSet;
		dsDataHeader = dsHeader.getDataTableHeader();
		
		//dataProvider = LocalDataProvider.getInstance();
		
		this.dsPartition = dataSet.getDataSetPartitionByID(localTablePartitionID);
		
		if(this.dsPartition == null) {
			throw new DataSetException("LocalTableDataProvider: Invalid local partition ID [" + localTablePartitionID + "]");
		}
		
		try { 
			dsPartition.setLocalTable(new LocalTable(dsPartition, NodeConfigurationManager.getInstance().getDataDataStore(), dsHeader.getDataName()+"_data"));
			dsPartition.setLocalIndex(new LocalIndex(dsPartition, NodeConfigurationManager.getInstance().getDataDataStore(), dsHeader.getDataName()+"_idx"));
		} catch(Exception e) {
			throw new DataSetException(e.getMessage());
		}
	}
	
	// TODO temporary quick fix
	public LocalTableDataProvider(DataSet dataSet) throws DataSetException {
		this(dataSet, 0);
	}
	
	public final void updateIntoDataSet(String[] columns, Object[] values) throws DataSetException {
		
		try { 
			int priKeyDataColumn = dsDataHeader.getPrimaryKey();
			byte priKeyType = dsDataHeader.getColumnType(priKeyDataColumn);
			String priKeyName = dsDataHeader.getColumnName(priKeyDataColumn);
			
			int priKeyInputColumn = -1;
			Object priKeyValue = new Integer(0); 
			
			Object[] typedValues = new Object[values.length];

			// reformat values to appropriate types
			// determine which input col contains the primary key
			for(int col = 0; col<columns.length; col++) {
				
				String colValue = (String)values[col];
				byte colType = dsDataHeader.getColumnType(col);
				
				// parse input strings
				switch(colType) {
					case ColumnType.COLUMN_TYPE_INT:
					case ColumnType.COLUMN_TYPE_PRIKEY_INT:
					case ColumnType.COLUMN_TYPE_PRIKEY_INT_AUTOINCREMENT:
						
						colValue = colValue.replaceAll("\\s", "");
						if(colValue.length() > 0) {
							try {
								typedValues[col] = new Integer(colValue);
							} catch (NumberFormatException e) {
								throw new DataSetException("Invalid input '" + colValue + "' on "+dsDataHeader.getColumnTypeString(col)+" column " + dsDataHeader.getColumnName(col) + ".");
							}
						} else {
							typedValues[col] = null;
						}
						break;
				
					case ColumnType.COLUMN_TYPE_SHORT: 
					case ColumnType.COLUMN_TYPE_PRIKEY_SHORT:
					case ColumnType.COLUMN_TYPE_PRIKEY_SHORT_AUTOINCREMENT:
						colValue = colValue.replaceAll("\\s", "");
						if(colValue.length() > 0) {
							try {
								typedValues[col] = new Short(colValue);
							} catch (NumberFormatException e) {
								throw new DataSetException("Invalid input '" + colValue + "' on "+dsDataHeader.getColumnTypeString(col)+" column " + dsDataHeader.getColumnName(col) + ".");
							}
						} else {
							typedValues[col] = null;
						}
						break;
						
					case ColumnType.COLUMN_TYPE_LONG: 
					case ColumnType.COLUMN_TYPE_PRIKEY_LONG:
					case ColumnType.COLUMN_TYPE_PRIKEY_LONG_AUTOINCREMENT:
						colValue = colValue.replaceAll("\\s", "").replaceAll("\\.|\\+", "");
						if(colValue.length() > 0) {
							try {
								typedValues[col] = new Long(colValue);
							} catch (NumberFormatException e) {
								throw new DataSetException("Invalid input '" + colValue + "' on "+dsDataHeader.getColumnTypeString(col)+" column " + dsDataHeader.getColumnName(col) + ".");
							}
						} else {
							typedValues[col] = null;
						}
						break;
						
					case ColumnType.COLUMN_TYPE_VARCHAR:
					case ColumnType.COLUMN_TYPE_PRIKEY_VARCHAR:
						typedValues[col] = colValue.replaceAll("^\\s*$", "");
						break;
				}
							
				
				if(columns[col].matches(priKeyName)) {
					priKeyInputColumn = col;
					priKeyValue = typedValues[col];
				}
			}
			if(priKeyInputColumn == -1) {
				throw new DataSetException("Primary Key value must be specified.");
			}
		
			// determine next value if autoincrement
			if((priKeyType == ColumnType.COLUMN_TYPE_PRIKEY_INT_AUTOINCREMENT) || (priKeyType == ColumnType.COLUMN_TYPE_PRIKEY_SHORT_AUTOINCREMENT)) {
				if(priKeyValue == "^$") {
					priKeyValue = dsDataHeader.getNextAutoIncrementValue();
				}
			}
			
			// loop thru each indicated column
			for(int a=0; a<columns.length; a++) {
				String colName = columns[a];
				int dataTableColumnID = dsDataHeader.getColumnIndexByName(colName);
				if(dataTableColumnID == -1) {
					throw new DataSetException("Column '"+colName+"' is not a column in '"+dsHeader.getName()+"'");
				}
				
				// update in data table
				Object inputValue = typedValues[a];
				
				TupleOperationStatus tos = dsPartition.getLocalTable().setValueByPrimaryKey(priKeyValue, dataTableColumnID, inputValue);
				if(tos.affectedTupleID != null) {
					dsPartition.getLocalIndex().updateColumnStatisticsByValue(dataTableColumnID, inputValue);
					dsPartition.getLocalIndex().addValueToBitmapIndex(dataTableColumnID, tos.affectedTupleID, inputValue);
				}
			}
			
		} catch (Exception e) {
			throw new DataSetException(e.getMessage());
		}
	}
	
	public final ResultSet getFullTableDataResultSet(String stringEncoding) throws DataSetException {
		try { 
			return dsPartition.getLocalTable().getFullTableDataResultSet(stringEncoding);
		} catch(Exception e) {
			throw new DataSetException("getFullTableDataResultSet: "+e.getMessage());
		}
	}
	
	public final ResultSet getIndexStatisticsResultSet(String stringEncoding) throws DataSetException {
		try { 
			ColumnIndexStatistics[] colIndexStats = tableIndex.generateColumnStatistics();
			return tableIndex.convertColumnStatsToResultSet(colIndexStats, stringEncoding);
		} catch(Exception e) {
			throw new DataSetException("getFullTableDataResultSet: "+e.getMessage());
		}
	}
	
	public final void addBitmapIndex(String[] columns) throws DataSetException {
		try { 
			
			tableIndex.buildBitmapIndex(columns);
		} catch(Exception e) {
			throw new DataSetException("getFullTableDataResultSet: "+e.getMessage());
		}
		
	}
	
}
